繼續昨天「針對不同的URL來路由(Routing)到對應的cache策略」,這裡我要將兩種不同的URL分別存入獨立的sub-cache中。第一個為material design CDN,另一個是從firebase storage取回的圖片URL。
workboxSW.router.registerRoute('https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css', workboxSW.strategies.staleWhileRevalidate({
cacheName: 'material-css'
}));
workboxSW.router.registerRoute(/.*(?:firebasestorage\.googleapis)\.com.*$/, workboxSW.strategies.staleWhileRevalidate({
cacheName: 'post-images'
}));
在workboxSW.router.registerRoute()
的第一個參數除了能輸入正規表示法外,也可以指定輸入「一般的URL字串」,所以這裡將material design CDN和URL中有出現firebasestorage.googleapis.com分別加入到material-css
和post-images
兩個sub-cache中。
記得現在每次更新完程式碼後要執行
npm run generate-sw
指令將新的Manifest注入到service worker中
除了staleWhileRevalidate
的cache策略,另外還有像是cacheFirst
、cacheOnly
、networkFirst
以及networkOnly
策略可以使用。
在每個策略中,我們可以針對這個cache去設定一些選項,之前已經示範過設定cacheName了,這裡來看看還可以設定甚麼其他選項:
workboxSW.router.registerRoute(/.*(?:googleapis|gstatic)\.com.*$/, workboxSW.strategies.staleWhileRevalidate({
cacheName: 'google-fonts',
cacheExpiration: {
maxEntries: 3,
maxAgeSeconds: 60 * 60 * 24 * 30
}
}));
開始設定googlefonts這個sub-cache的「有效筆數和期限」。這裡我最多只暫存3筆資料,以及有效期限為1個月(以秒數計算)。
來看一下執行結果吧:
我們可以發現當設定完有效期限後,workbox會自動在indexedDB中新增一個workbox-cache-expiration
的table(key為google fonts url,value則是timestamp object)。
我們還有一個URL還沒有進行Routing處理,那就是Firebase RealTime Database API
。在原始的sw.js file中,我將從這個API fetch回來的貼文資料暫存到indexedDB中。這裡我就直接依照原本的路由邏輯在新的service worker中添加程式碼:
workboxSW.router.registerRoute('https://trip-diary-f56de.firebaseio.com/posts.json', function(args) {
return fetch(args.event.request).then(function(res) {
var clonedRes = res.clone();
clearAllData('posts').then(function() {
return clonedRes.json();
}).then(function(data) {
for(var key in data) {
writeData('posts', data[key]);
}
});
return res;
});
});
workboxSW.router.registerRoute的第2個參數除了使用workbox提供的cache策略外,也可以自己寫一個callback function。這裡workbox會幫我們監聽fetch event並將事件傳入args這個參數中,之後的程式邏輯就跟原本寫在sw.js中相同。
最後,我們來在新的service worker中實作當靜態資源(像是project中的help.html)沒有被暫存時,應該要出現的fallback頁面。
非常可惜的是workbox並沒有提供我們簡單的方式來實作fallback頁面,所以這裡必須要自己來撰寫:
workboxSW.router.registerRoute(function(routeData) {
return (routeData.event.request.headers.get('accept').includes('text/html'));
}, function(args) {
return caches.match(args.event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(args.event.request).then(function(res) {
return caches.open('dynamic').then(function(cache) {
cache.put(args.event.request.url, res.clone());
return res;
})
}).catch(function(err) {
return caches.match('/offline.html').then(function(res) {
return res;
});
});
}
})
});
workboxSW.router.registerRoute()
的第1個參數也可以輸入一個函式,這個函式主要是去定義我們要Routing的request類型,這裡我設定成只要去路由「headers中的accept欄位是html」的request。
接著第2個參數的函式可以直接依照之前sw.js中fetch event listener的else區塊中程式碼來修改。
cache邏輯基本上都是相同的,首先到cache中尋找有無暫存,有的話則回傳;沒有的話就透過網路去fetch回來,並把結果暫存到dynamic sub-cache中。
如果在cache中沒有暫存且也沒有網路連線的話,就回傳我們預先pre-cache好的offline.html
頁面。
來看一下offline畫面的結果吧:
Day29 結束!!